Understanding Classes and Objects in Python
Course: Python Training
Topic: Understanding Classes and Objects
Course Link: Python Training Course
Demo Link: Register for a Course Demo
Website: JustAcademy
1. Introduction to Classes and Objects
Classes and objects are fundamental concepts in Object-Oriented Programming (OOP). Python supports object-oriented programming, allowing developers to organize data and functionality into reusable structures.
A class defines the attributes and behaviors that its objects can have. An object is an instance created from a class. Objects can store their own data and use methods defined by their class.
For example, consider a student management application. Every student may have a name, age, course, and marks. Instead of creating separate variables for every student, we can define a Student class and create multiple student objects from it.
class Student:
pass
student1 = Student()
student2 = Student()
print(type(student1))
print(type(student2))
Output:
<class '__main__.Student'>
<class '__main__.Student'>
The Student class defines a new type. The variables student1 and student2 refer to two separate instances of that class.
2. What Is a Class?
A class is a blueprint or template used to create objects. It groups related data and functions into a single structure.
A class commonly contains:
- Attributes: Variables that store information about a class or its objects.
- Methods: Functions defined inside a class that describe operations or behaviors.
- Constructor-related initialization: Code that prepares a new object with its initial state.
2.1 Basic Class Syntax
class ClassName:
# Attributes and methods
pass
The class keyword begins a class definition. ClassName is the name of the class, and the indented block contains its body. The pass statement is a placeholder that allows the class body to remain empty.
2.2 Example of a Simple Class
class Car:
brand = "Toyota"
color = "White"
car1 = Car()
print(car1.brand)
print(car1.color)
Output:
Toyota
White
In this example, Car is a class, and car1 is an instance of it. The attributes brand and color are defined in the class body and can be accessed through the object.
3. What Is an Object?
An object is a particular instance of a class. When an object is created, it can hold data describing that specific instance and use the methods available through its class.
For example, a Car class can be used to create many car objects. Each object can represent a different car with its own brand, color, and model.
class Car:
pass
car1 = Car()
car2 = Car()
car3 = Car()
print(car1)
print(car2)
print(car3)
Python displays object representations that commonly include the class name and an implementation-specific memory address. The objects are separate instances even though they come from the same class.
3.1 Real-World Examples
| Class |
Possible Attributes |
Possible Methods |
| Student |
Name, age, course, marks |
Display details, calculate grade |
| Car |
Brand, model, speed |
Accelerate, brake, display details |
| BankAccount |
Account holder, balance |
Deposit, withdraw, check balance |
| Employee |
Name, employee ID, salary |
Display profile, calculate bonus |
| Product |
Name, price, quantity |
Calculate total, update stock |
| Book |
Title, author, price |
Display information, update price |
4. Creating Objects from a Class
Objects are created by calling the class name as though it were a function. This process is known as instantiation.
Syntax:
object_name = ClassName()
4.1 Example
class Student:
name = "Aarav"
course = "Python"
student1 = Student()
print(student1.name)
print(student1.course)
Output:
Aarav
Python
Calling Student() creates an instance of the class. The object can then access its attributes using dot notation.
4.2 Creating Multiple Objects
class Employee:
company = "ABC Technologies"
employee1 = Employee()
employee2 = Employee()
employee1.name = "Riya"
employee2.name = "Karan"
print(employee1.name)
print(employee2.name)
print(employee1.company)
print(employee2.company)
Output:
Riya
Karan
ABC Technologies
ABC Technologies
Each employee object has its own dynamically assigned name attribute. Both objects can access the shared class attribute company.
5. Understanding the __init__() Method
The __init__() method initializes a newly created object. It is commonly used to assign starting values to instance attributes.
When you call a class to create an object, Python creates the instance and then invokes its initialization method. Technically, __init__() initializes an instance; it is not the method that creates the instance itself.
5.1 Syntax
class ClassName:
def __init__(self, parameter1, parameter2):
self.attribute1 = parameter1
self.attribute2 = parameter2
5.2 Example
class Student:
def __init__(self, name, age):
self.name = name
self.age = age
student1 = Student("Aarav", 20)
student2 = Student("Meera", 22)
print(student1.name, student1.age)
print(student2.name, student2.age)
Output:
Aarav 20
Meera 22
The constructor parameters provide initial values for each object. The first student stores the name Aarav and age 20, while the second student stores different values.
5.3 Why Use __init__()?
- It initializes object attributes automatically.
- It avoids repeating attribute assignments after every object is created.
- It makes the required data for an object clear.
- It helps ensure objects start in a consistent state.
6. Understanding self in Python
self refers to the current instance of a class. It allows an instance method to access or modify that particular object's attributes and call its other methods.
In Python, self is a conventional parameter name, not a reserved keyword. Instance methods normally declare it as their first parameter. When a method is called through an object, Python supplies the instance automatically.
class Student:
def __init__(self, name):
self.name = name
def display(self):
print("Student name:", self.name)
student1 = Student("Ananya")
student1.display()
Output:
Student name: Ananya
When student1.display() is called, Python passes student1 as the instance argument. Inside the method, self.name accesses that object's name.
6.1 Different Objects Have Different self Values
class Mobile:
def __init__(self, brand, price):
self.brand = brand
self.price = price
def show_details(self):
print(self.brand, self.price)
mobile1 = Mobile("Samsung", 25000)
mobile2 = Mobile("Apple", 65000)
mobile1.show_details()
mobile2.show_details()
Output:
Samsung 25000
Apple 65000
The same method works with both objects, but self refers to the instance on which the method is called.
7. Understanding Attributes in Python Classes
Attributes are named values associated with a class or an object. Python commonly uses two categories of attributes: instance attributes and class attributes.
7.1 Instance Attributes
Instance attributes belong to individual objects. They are commonly created using self inside __init__().
class Employee:
def __init__(self, name, salary):
self.name = name
self.salary = salary
employee1 = Employee("Riya", 40000)
employee2 = Employee("Karan", 50000)
print(employee1.name, employee1.salary)
print(employee2.name, employee2.salary)
Output:
Riya 40000
Karan 50000
Each object maintains its own name and salary attributes.
7.2 Class Attributes
Class attributes are defined in the class body and are shared through the class unless an instance overrides the same attribute name.
class Employee:
company = "Tech Solutions"
def __init__(self, name):
self.name = name
employee1 = Employee("Riya")
employee2 = Employee("Karan")
print(employee1.company)
print(employee2.company)
print(Employee.company)
Output:
Tech Solutions
Tech Solutions
Tech Solutions
Both objects can access the class attribute company. It can also be accessed directly through the class.
7.3 Instance Attributes vs Class Attributes
| Feature |
Instance Attribute |
Class Attribute |
| Defined commonly in |
__init__() using self |
Class body |
| Associated with |
Individual object |
Class and its instances through attribute lookup |
| Typical use |
Unique names, prices, ages |
Shared company name, category, configuration |
| Access |
object.attribute |
Class.attribute or object.attribute |
| Changing an instance value |
Usually affects that instance only |
Assigning through an instance may create an instance attribute that shadows the class attribute |
7.4 A Common Class Attribute Mistake
class Team:
members = []
team1 = Team()
team2 = Team()
team1.members.append("Asha")
print(team1.members)
print(team2.members)
Output:
['Asha']
['Asha']
Both objects refer to the same mutable list stored as a class attribute. If each object needs its own list, initialize it in __init__():
class Team:
def __init__(self):
self.members = []
team1 = Team()
team2 = Team()
team1.members.append("Asha")
print(team1.members)
print(team2.members)
Output:
['Asha']
[]
8. Defining Methods Inside a Class
A method is a function defined inside a class. Methods describe the actions that objects can perform. An instance method usually accepts self as its first parameter.
8.1 Example of an Instance Method
class Calculator:
def add(self, a, b):
return a + b
def subtract(self, a, b):
return a - b
calculator = Calculator()
print(calculator.add(10, 5))
print(calculator.subtract(10, 5))
Output:
15
5
The methods are accessed using dot notation. In this example, the operations do not need to read instance attributes, but they are still valid instance methods.
8.2 Methods That Use Object Data
class BankAccount:
def __init__(self, holder, balance):
self.holder = holder
self.balance = balance
def display_balance(self):
print(self.holder, "has balance", self.balance)
account = BankAccount("Rahul", 5000)
account.display_balance()
Output:
Rahul has balance 5000
The method accesses the object's attributes through self, allowing each bank account to display its own information.
9. Creating and Calling Methods with Parameters
Methods can accept parameters just like ordinary functions. Parameters allow an object to perform actions using values supplied when the method is called.
class Rectangle:
def __init__(self, length, width):
self.length = length
self.width = width
def area(self):
return self.length * self.width
def perimeter(self):
return 2 * (self.length + self.width)
rectangle = Rectangle(10, 5)
print("Area:", rectangle.area())
print("Perimeter:", rectangle.perimeter())
Output:
Area: 50
Perimeter: 30
The area() and perimeter() methods use the object's stored dimensions rather than requiring the dimensions to be passed again.
10. Modifying Object Attributes
Instance attributes can be modified after an object is created. Updating an attribute changes the state of that particular object.
class Product:
def __init__(self, name, price):
self.name = name
self.price = price
product1 = Product("Keyboard", 1200)
print(product1.price)
product1.price = 1500
print(product1.price)
Output:
1200
1500
The assignment product1.price = 1500 updates the price stored on that object.
10.1 Updating Attributes Through a Method
class Product:
def __init__(self, name, price):
self.name = name
self.price = price
def update_price(self, new_price):
if new_price >= 0:
self.price = new_price
else:
print("Price cannot be negative")
product = Product("Mouse", 700)
product.update_price(850)
print(product.price)
Output:
850
Using a method for updates provides a place to validate input and maintain the object's expected state.
11. Accessing and Deleting Attributes
Python provides built-in functions such as getattr(), setattr(), hasattr(), and delattr() for working with attributes dynamically.
class Student:
def __init__(self, name):
self.name = name
self.course = "Python"
student = Student("Meera")
print(getattr(student, "name"))
print(hasattr(student, "course"))
setattr(student, "course", "Data Analytics")
print(student.course)
delattr(student, "course")
print(hasattr(student, "course"))
Output:
Meera
True
Data Analytics
False
These functions are useful when attribute names are determined dynamically. For ordinary code, direct dot notation is often clearer.
12. The Difference Between a Class and an Object
A class describes a type of object, while an object is a specific instance of that type.
| Class |
Object |
| Defines attributes and methods |
Uses the attributes and methods provided by its class |
| Acts as a template for instances |
Is an individual instance created from a class |
Defined using the class keyword |
Created by calling the class |
Example: class Student: |
Example: student1 = Student() |
| Can define common behavior |
Can store instance-specific state |
Example
class Laptop:
def __init__(self, brand, ram):
self.brand = brand
self.ram = ram
laptop1 = Laptop("Dell", "8GB")
laptop2 = Laptop("HP", "16GB")
print(laptop1.brand, laptop1.ram)
print(laptop2.brand, laptop2.ram)
Output:
Dell 8GB
HP 16GB
Laptop is the class. laptop1 and laptop2 are separate objects with different attribute values.
13. The __str__() Method
The special method __str__() defines a human-readable string representation of an object. It is used by str() and by print() when displaying the object.
class Student:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f"{self.name}, age {self.age}"
student = Student("Aarav", 20)
print(student)
Output:
Aarav, age 20
Without a custom __str__() method, printing a custom object commonly produces a default representation that is less informative to users.
14. Understanding the __repr__() Method
The __repr__() method defines an object representation intended primarily for debugging and development. A good representation often makes it clear what kind of object is being displayed and which values it contains.
class Product:
def __init__(self, name, price):
self.name = name
self.price = price
def __repr__(self):
return f"Product(name={self.name!r}, price={self.price!r})"
product = Product("Keyboard", 1200)
print(repr(product))
Output:
Product(name='Keyboard', price=1200)
When a class defines __repr__() but not __str__(), the representation can also be used by str() as a fallback.
15. Understanding Object Identity and Type
Python provides built-in functions to inspect an object's type and identity.
type(object) returns the object's type.
isinstance(object, ClassName) checks whether an object is an instance of a class or one of its subclasses.
id(object) returns an identity value that is unique and constant for that object's lifetime.
is checks whether two references point to the same object.
class Student:
pass
student1 = Student()
student2 = Student()
student3 = student1
print(type(student1))
print(isinstance(student1, Student))
print(student1 is student2)
print(student1 is student3)
Output:
<class '__main__.Student'>
True
False
True
student1 and student2 refer to separate objects. The variable student3 refers to the same object as student1.
16. Understanding Object References and Aliasing
In Python, variables refer to objects rather than acting as named containers that always hold independent copies. Assigning one object reference to another variable creates an additional reference to the same object.
class Student:
def __init__(self, name):
self.name = name
student1 = Student("Riya")
student2 = student1
student2.name = "Aman"
print(student1.name)
print(student2.name)
Output:
Aman
Aman
Both variables refer to the same object. Therefore, changing its name attribute through student2 is visible through student1 as well.
If you need an independent object, create a new instance or use an appropriate copying strategy.
17. Encapsulation: Keeping Data and Behavior Together
Encapsulation is an object-oriented programming principle that groups data and the methods that operate on it. It also encourages controlling how an object's internal state is accessed and changed.
Python does not enforce traditional private access in exactly the same way as some other languages. By convention, an attribute beginning with a single underscore, such as _balance, is treated as non-public. Double-leading-underscore names trigger name mangling, which helps avoid accidental name conflicts but does not provide absolute security.
class BankAccount:
def __init__(self, holder, balance=0):
self.holder = holder
self._balance = balance
def deposit(self, amount):
if amount > 0:
self._balance += amount
else:
print("Deposit must be positive")
def get_balance(self):
return self._balance
account = BankAccount("Riya", 1000)
account.deposit(500)
print(account.get_balance())
Output:
1500
The deposit method validates the amount before changing the balance. This design encourages callers to use the provided behavior rather than update the balance arbitrarily.
18. Class Methods and Static Methods
Python supports instance methods, class methods, and static methods. Each has a different purpose.
18.1 Instance Method
An instance method receives the current object as its first argument, conventionally named self. It is useful when the method needs to read or modify instance attributes.
18.2 Class Method
A class method uses the @classmethod decorator and receives the class as its first argument, conventionally named cls. It is useful for working with class-level data or providing alternative ways to construct objects.
18.3 Static Method
A static method uses the @staticmethod decorator. It does not automatically receive an instance or class argument. It is useful for a related utility function that belongs conceptually to the class.
class Employee:
company = "ABC Technologies"
def __init__(self, name, salary):
self.name = name
self.salary = salary
def display(self):
return f"{self.name}: {self.salary}"
@classmethod
def company_name(cls):
return cls.company
@staticmethod
def is_valid_salary(salary):
return salary >= 0
employee = Employee("Aman", 45000)
print(employee.display())
print(Employee.company_name())
print(Employee.is_valid_salary(45000))
Output:
Aman: 45000
ABC Technologies
True
| Method Type |
First Automatic Argument |
Typical Purpose |
| Instance method |
self |
Operate on an individual object |
| Class method |
cls |
Work with class-level information or alternate constructors |
| Static method |
None |
Provide a related utility operation |
19. Introduction to Inheritance
Inheritance allows a new class to reuse or extend behavior defined by another class. The existing class is called the base or parent class, and the new class is called the derived or child class.
class Animal:
def eat(self):
print("This animal eats food")
class Dog(Animal):
def bark(self):
print("The dog barks")
dog = Dog()
dog.eat()
dog.bark()
Output:
This animal eats food
The dog barks
The Dog class inherits the eat() method from Animal and defines its own bark() method.
20. Method Overriding
Method overriding occurs when a child class defines a method with the same name as a method inherited from its parent class. The child's implementation is used when that method is called on a child object.
class Animal:
def sound(self):
print("Animal makes a sound")
class Dog(Animal):
def sound(self):
print("Dog barks")
animal = Animal()
dog = Dog()
animal.sound()
dog.sound()
Output:
Animal makes a sound
Dog barks
Overriding lets related classes provide specialized behavior while keeping a common method interface.
21. Using super() in Inheritance
The built-in super() function allows a class to call an implementation from the next class in the method resolution order. It is often used to extend initialization or behavior inherited from a parent class.
class Person:
def __init__(self, name):
self.name = name
class Student(Person):
def __init__(self, name, course):
super().__init__(name)
self.course = course
def display(self):
print(self.name, self.course)
student = Student("Meera", "Python")
student.display()
Output:
Meera Python
The call to super().__init__(name) runs the parent initialization logic, after which the child class sets its own course attribute.
22. Abstraction and Polymorphism: A Brief Overview
Classes and objects provide a foundation for other object-oriented programming principles.
22.1 Abstraction
Abstraction focuses on exposing essential operations while hiding unnecessary implementation details. Python supports abstract base classes through the abc module.
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
pass
class Square(Shape):
def __init__(self, side):
self.side = side
def area(self):
return self.side ** 2
square = Square(4)
print(square.area())
Output:
16
An abstract class with an unimplemented abstract method cannot normally be instantiated until a concrete subclass implements the required method.
22.2 Polymorphism
Polymorphism allows different object types to respond to the same method call in their own way.
class Cat:
def sound(self):
return "Meow"
class Dog:
def sound(self):
return "Bark"
animals = [Cat(), Dog()]
for animal in animals:
print(animal.sound())
Output:
Meow
Bark
Both objects provide a sound() method, but each returns a different result.
23. Common Errors When Working with Classes and Objects
| Error |
Cause |
Solution |
NameError |
Using an undefined class or variable name |
Check spelling, scope, and definition order |
AttributeError |
Accessing an attribute that does not exist |
Check initialization and attribute spelling |
TypeError during object creation |
Providing the wrong number of constructor arguments |
Match the arguments expected by __init__() |
Missing self |
Defining an instance method without the expected instance parameter |
Include self as the first parameter of instance methods |
| Unexpected shared data |
Using a mutable class attribute for per-object data |
Initialize separate mutable values in __init__() |
| Unexpected change across variables |
Two variables refer to the same object |
Create a new object or use an appropriate copy operation |
Method returns None |
A method prints a value but does not return one |
Use return when the caller needs a result |
24. Best Practices for Writing Classes
- Use class names written in CapWords style, such as
StudentRecord or BankAccount.
- Use clear, descriptive names for attributes and methods.
- Use
__init__() to initialize the object's required state.
- Use
self consistently to access instance attributes and methods.
- Keep each class focused on a clear responsibility.
- Use instance attributes for data that should be independent for each object.
- Use class attributes for genuinely shared data, especially immutable configuration values.
- Validate important input before modifying object state.
- Use
__str__() and __repr__() when useful representations improve debugging or display.
- Use inheritance when there is a genuine relationship between types, not merely to share unrelated code.
- Prefer composition when one object should contain or use another object without being a specialized version of it.
- Write small methods that are easy to test and maintain.
25. Mini Project: Student Management System
Project objective: Build a simple student management program using a class, instance attributes, methods, and multiple objects.
class Student:
school = "Bright Future Academy"
def __init__(self, name, roll_number, marks):
self.name = name
self.roll_number = roll_number
self.marks = marks
def calculate_grade(self):
if self.marks >= 90:
return "A"
elif self.marks >= 75:
return "B"
elif self.marks >= 60:
return "C"
elif self.marks >= 40:
return "D"
else:
return "F"
def display_details(self):
print("School:", self.school)
print("Name:", self.name)
print("Roll Number:", self.roll_number)
print("Marks:", self.marks)
print("Grade:", self.calculate_grade())
print("-" * 25)
student1 = Student("Aarav", 101, 92)
student2 = Student("Meera", 102, 78)
student3 = Student("Karan", 103, 55)
student1.display_details()
student2.display_details()
student3.display_details()
Output:
School: Bright Future Academy
Name: Aarav
Roll Number: 101
Marks: 92
Grade: A
-------------------------
School: Bright Future Academy
Name: Meera
Roll Number: 102
Marks: 78
Grade: B
-------------------------
School: Bright Future Academy
Name: Karan
Roll Number: 103
Marks: 55
Grade: D
-------------------------
Project Explanation
- The
Student class defines the structure and behavior of student objects.
- The class attribute
school stores a school name shared by all students.
- The
__init__() method initializes each student's name, roll number, and marks.
- The
calculate_grade() method determines a grade based on marks.
- The
display_details() method prints the student's information and calculated grade.
- Three independent student objects are created with different data.
This project demonstrates how classes help organize related information and behavior in a reusable structure.
26. Practice Exercises
- Create a
Book class with attributes for title, author, and price. Create two objects and display their details.
- Create a
Rectangle class with length and width attributes. Add methods to calculate area and perimeter.
- Create an
Employee class with name, ID, and salary. Add a method that displays employee details.
- Create a
BankAccount class with deposit and withdrawal methods. Prevent withdrawals that exceed the available balance.
- Create a
Mobile class with brand, model, and price. Add a method to apply a discount.
- Demonstrate the difference between class attributes and instance attributes.
- Create a class with a custom
__str__() method.
- Build a parent class named
Vehicle and a child class named Bike.
- Demonstrate method overriding with two related classes.
- Create a student management program that calculates grades for multiple students.
27. Interview Questions and Answers
Q1. What is a class in Python?
A class is a user-defined type that groups related attributes and methods. It serves as a template for creating instances.
Q2. What is an object?
An object is an instance of a class. It can hold state through attributes and provide behavior through methods.
Q3. What is the difference between a class and an object?
A class defines the structure and behavior, while an object is an individual instance created from that class.
Q4. What is __init__()?
__init__() is an initialization method that runs after a new instance is created. It is commonly used to assign initial attribute values.
Q5. What is self in Python?
self conventionally refers to the current instance inside an instance method. Python supplies it automatically when the method is called through an object.
Q6. What are instance attributes?
Instance attributes belong to individual objects. They are commonly initialized using assignments such as self.name = name.
Q7. What are class attributes?
Class attributes are defined in the class body and are available through the class and its instances unless shadowed by an instance attribute.
Q8. What is an instance method?
An instance method is a function defined inside a class that normally receives the current object as its first parameter, conventionally named self.
Q9. What is inheritance?
Inheritance allows a child class to reuse or extend attributes and methods from a parent class.
Q10. What is method overriding?
Method overriding occurs when a child class defines a method with the same name as an inherited method, providing its own implementation.
Q11. What is the purpose of super()?
super() provides access to implementations in the next class in the method resolution order. It is commonly used to extend inherited initialization or behavior.
Q12. What is encapsulation?
Encapsulation groups data and related methods together and encourages controlled access to an object's internal state.
Q13. What is the difference between __str__() and __repr__()?
__str__() aims to provide a readable representation for users. __repr__() aims to provide a useful representation for debugging and development.
Q14. What is a class method?
A class method is defined with @classmethod and receives the class as its first argument, conventionally named cls.
Q15. What is a static method?
A static method is defined with @staticmethod and does not automatically receive the instance or class as an argument.
28. Quick Revision Checklist
- A class defines a type and serves as a template for instances.
- An object is an instance of a class.
- Use the
class keyword to define a class.
- Call the class name to create an object.
- Use
__init__() to initialize instance attributes.
self refers to the current instance in instance methods.
- Instance attributes store object-specific data.
- Class attributes provide shared values through the class.
- Methods define the behavior associated with a class.
__str__() and __repr__() provide object representations.
isinstance() checks whether an object is an instance of a given type.
- Inheritance supports reuse and specialization.
- Encapsulation organizes data and behavior and encourages controlled state changes.
- Class methods use
cls, while static methods do not receive an automatic instance or class argument.
29. Conclusion
Classes and objects are essential building blocks of object-oriented programming in Python. Classes define the attributes and methods that describe a type, while objects represent individual instances with their own state.
By learning to create classes, initialize objects with __init__(), use self, define methods, and distinguish class attributes from instance attributes, you can build programs that are organized, reusable, and easier to maintain. Concepts such as inheritance, encapsulation, abstraction, and polymorphism extend these foundations for larger applications.
Continue your learning with the Python Training Course, register through the JustAcademy Course Demo page, or visit the JustAcademy website for additional learning resources.